module Pan
  
  class Component
    attr_accessor :owner
    attr_accessor :parent

    def name
      parent.name
    end
    
    def init 
    end

    ## Call / Answer

    def call(comp)
      callcc {|k|
        comp.k = k
        comp.owner = self
        push(comp)
        throw :call_escape_tag
      }
    end

    attr_accessor :k
    def answer(value=nil)
      @owner.pop 
      @k.call(value)
    end
    
    ###
    
    def session
      Dynamic.current_session
    end

    def root
      session.root
    end
    
    def render(html)
      render_content_on(html)
    end
    
    def render_members(vars_and_values, html)
      html.table("border"=>"1") {
        vars_and_values.each do |var, val|
          html.tr {
            html.td {
              html.b {
                html.text(var)
              }
            }
            html.td {
              if val.atom?
                html.text(val.inspect)
              else
                html.text(val.class.to_s)
              end
            }
          }
        end
      }
    end

    ###
    def inform(string)
      self.call(SimpleDialog.new.add_message(string))
    end

    def confirm(msg)
      dialog = YesNoDialog.new
      dialog.add_message(msg)
      call(dialog)
    end
    
    ### Layers
    
    def layers
      @layers = [] unless @layers
      @layers
    end

    def top_layer
      if height > 0
        @layers.last
      else
        self
      end
    end

    def push(comp)
      layers.push(comp)
    end
    
    def pop
      layers.pop if @layers
    end
    
    def pop_all
      @layers = []
    end

    def height
      layers.size
    end

    def template
      name = "templates/#{self.class.to_s.snake_case.split("::").join("_")}.html"
      ERB.new(IO.read(name))
    end

    def render_template(html)
      html.text(template().result(binding))
    end

    def render_top(html)
      html.render(top_layer)
    end
    
    def render_content_on(html)
      begin
        if @layers and @layers.size > 0
          render_top(html)
        else
          if session.inspectors
            if top_layer.class != ObjectInspector && top_layer.class != Inspector
              html.div(:class=>'object-inspector') {
                html.link("[#{top_layer.class.to_s}]") { 
                  root.call(Inspector.new(top_layer)) 
                } 
              }
            end
          end
          render_on(html)
        end
      rescue Exception => e
        puts e.message
        push(ErrorInformer.new(e, self))
        render_top(html)
      end
    end
    
    def _render_content_on(html)
      if $debug
        html.div("style"=>{"border"=>"3px solid"}) {
          vars = instance_variables()
          values = vars.map{ |name| instance_variable_get(name)}
          html.heading(self.class.to_s,4)
          render_members(vars.zip(values), html)
          html.hr
          render_on(html)
        }
      else
        render_on(html)
      end
    end

    def subclass_responsibility(method=nil)
      raise Exception.new("Subclass must implement '#{method||'this method'}")
    end

    def render_on(html)
      subclass_responsibility('render_on')
    end
    
    def direct_render
      html = HtmlCanvas.new(nil)
      yield(html)    
      ## FIX: should use a method in session
      Dynamic.current_response.start(200) do |head,out|
        head["Content-Type"] = "text/html"
        out.write(html.to_html)
      end
    end
    
    def deep_copy
      Marshal.load( Marshal.dump( self ) )
    end

    def callbacks_done(chain)
      # puts "callbacks_done hit leaf: #{(chain << self).map{|ea| ea.class.name}.inspect}"
    end
  end
end
